import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;

public class VolatileVSAtomicReferenceTest {
    static class Node {
        long value;
        AtomicReference<Node> next;

        Node(long value) {
            this.value = value;
            this.next = new AtomicReference<>();
        }
    }

    static class Node2 {
        long value;
        volatile Node2 next;

        Node2(long value) {
            this.value = value;
            this.next = null;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        final int opNum = 1000000;
        final Node root1 = new Node(1);
        final Node2 root2 = new Node2(1);
        Node node = root1;
        for (int i = 2; i < opNum; i++) {
            Node node2 = new Node(i);
            node.next.set(node2);
            node = node2;
        }
        Node2 node21 = root2;
        for (int i = 2; i < opNum; i++) {
            Node2 node22 = new Node2(i);
            node21.next = node22;
            node21 = node22;
        }

        final int threadNum = 10;
        CountDownLatch latch = new CountDownLatch(threadNum);
        for (int i = 0; i < threadNum; i++) {
            Thread thread = new Thread(() -> {
                for (int h = 0; h < 3; h++) {
                    {
                        Instant startTime = Instant.now();
                        for (int j = 0; j < 500; j++) {
                            Node2 node_ = root2;
                            for (int k = 0; k < opNum - 3; k++) {
                                node_ = node_.next;
                            }
                        }
                        Instant endTime = Instant.now();
                        Duration duration = Duration.between(startTime, endTime);
                        System.out.println("Volatile (ms): " + duration.toMillis());
                    }
                    {
                        Instant startTime = Instant.now();
                        for (int j = 0; j < 500; j++) {
                            Node node_ = root1;
                            for (int k = 0; k < opNum - 3; k++) {
                                node_ = node_.next.get();
                            }
                        }
                        Instant endTime = Instant.now();
                        Duration duration = Duration.between(startTime, endTime);
                        System.out.println("AtomicReference (ms): " + duration.toMillis());
                    }
                }
                latch.countDown();
            });
            thread.start();
        }
        latch.await();
    }
}
